package com.nulldev.util.crypto.api3.encryptors.p2pe2;

import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.regex.Pattern;

import com.nulldev.util.crypto.api2.Cryptable;
import com.nulldev.util.io.MD5Utils;
import com.nulldev.util.io.ZipUtils;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.math.MathUtil;
import com.nulldev.util.other.ArrayUtils;
import com.nulldev.util.other.ByteUtils;
import com.nulldev.util.string.StringsUtil;

import static com.nulldev.util.simplejava.SimpleJava.*;

@Deprecated
public class P2PE2C extends Cryptable {
	public P2PE2C() {
		super("p2pE.v2.compress");
	}

	@Override
	public String encrypt(String object, String pass) {
		final BigInteger[] split = MathUtil.getValsB(object);
		final String metadata = "+_+_" + MD5Utils.getMD5HexT(pass);
		Integer[] pword = MathUtil.getVals(pass);
		for (int cx = pword.length - 1; cx > 0; cx--) {
			//println("cx=" + cx + ", length=" + pword.length);
			pword[cx] = pword[cx] + 
					pword[(pword.length - 1) - (cx - 1)];
		}
		final BigInteger pwordHash = MathUtil.hashString(pass);
		for (int i = 0; i < split.length; i++) {
			final BigInteger val = split[i];
			BigInteger newVal = val;
			// forward encrypt a-->b
			for (final int p : pword) {
				newVal = newVal.multiply(new BigInteger(String.valueOf(p)));
			}
			// reverse encrypt a<--b
			for (int px = pword.length - 1; px > 0; px--) {
				final int p = pword[px];
				newVal = newVal.multiply(new BigInteger(String.valueOf(p)));
			}
			// add hash
			newVal = newVal.add(pwordHash);
			split[i] = newVal;
		}
		return ByteUtils.toBase64(ZipUtils.zip(ArrayUtils.toSList(split) + metadata));
	}

	@Override
	public String encrypt(String object, String pass, Charset charset) {
		return this.encrypt(object, pass);
	}

	@Override
	public String decrypt(String object, String pass) {
		object = ZipUtils.unzip(ByteUtils.fromBase64(object));
		final String[] data = object.split(Pattern.quote("+_+_"));
		String metadata = "";
		try {
			metadata = data[1];
		}catch (Exception e) {
			e.printStackTrace();
		}
		object = data[0];
		if (!MD5Utils.getMD5HexT(pass).equals(metadata)) {
			getLogger().error("[Type.ERROR] decrypt(Metadata.MD5) -> Hash mismatch!");
			return new String(MathUtil.randomBytes(ByteUtils.create(256)));
		}
		final BigInteger[] split = MathUtil.getValsBi(object);
		final BigInteger pwordHash = MathUtil.hashString(pass);
		final Integer[] pword = MathUtil.getVals(pass);
		for (int cx = pword.length - 1; cx > 0; cx--) {
			pword[cx] = pword[cx] + 
					pword[(pword.length + 1) - (cx + 1)];
			//debugC("decrypt: " + String.valueOf(pword[cx]));
		}
		//ArrayUtils.println(pword);
		for (int i = 0; i < split.length; i++) {
			final BigInteger val = split[i];
			BigInteger newVal = val;
			// reverse decrpyt b-->a
			for (int px = pword.length - 1; px > 0; px--) {
				final int p = pword[px];
				try {
					newVal = newVal.divide(new BigInteger(String.valueOf(p)));
				}catch (ArithmeticException e) {
					log.error("Expected decrypt failure. Reason: " + e.getMessage() + " | p = " + (p) + ", newVal = " + newVal.toString());
				}
			}
			// forward decrypt a-->b
			for (final int p : pword) {
				try {
					newVal = newVal.divide(new BigInteger(String.valueOf(p)));
				}catch (ArithmeticException e) {
					log.error("Expected decrypt failure. Reason: " + e.getMessage() + " | p = " + (p) + ", newVal = " + newVal.toString());
				}
			}
			newVal = newVal.subtract(pwordHash);
			split[i] = newVal;
		}
		return StringsUtil.toStandard(split);
	}

	@Override
	public String decrypt(String object, String pass, Charset charset) {
		return this.decrypt(object, pass);
	}

	@Override
	public String toPGPString(final String in) {
		final StringBuilder b = new StringBuilder();
		b.append("--START P2P BLOCK---").
			append(JVMUtil.lineSep()).
			append("Type: EC").
			append(JVMUtil.lineSep()).
			append("Iterations: 1").
			append(JVMUtil.lineSep()).
			append("Data: {" + in + "}").
			append(JVMUtil.lineSep()).
			append("----END P2P BLOCK---");
		return b.toString();
	}
	
	@Override
	public String toPGPString(final String in, final String key) {
		final StringBuilder b = new StringBuilder();
		b.append("--START P2P BLOCK---").
			append(JVMUtil.lineSep()).
			append("Type: EC").
			append(JVMUtil.lineSep()).
			append("Iterations: 1").
			append(JVMUtil.lineSep()).
			append("Key: " + key).
			append(JVMUtil.lineSep()).
			append("Data: {" + in + "}").
			append(JVMUtil.lineSep()).
			append("----END P2P BLOCK---");
		return b.toString();
	}
}
